﻿using System;
using System.Collections.Generic;
using SpiceNet.Diagnostics;
using SpiceNet.Circuits;
using SpiceNet.Parameters;

namespace SpiceNet.Models.Waveforms
{
    /// <summary>
    /// A waveform that generates a sinewave
    /// </summary>
    public class Sine : Waveform
    {
        /// <summary>
        /// Private variables
        /// </summary>
        private double vo, va, freq, td, theta;

        #region Parameters
        private enum _c { SINE_VO = 1, SINE_VA = 2, SINE_FREQ = 3, SINE_DELAY = 4, SINE_THETA = 5 }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "vo", new IP(IF.IOP, (int)_c.SINE_VO, T.REAL, "The offset of the sine wave") },
            { "va", new IP(IF.IOP, (int)_c.SINE_VA, T.REAL, "The amplitude of the sine wave") },
            { "freq", new IP(IF.IOP, (int)_c.SINE_FREQ, T.REAL, "The frequency in Hertz") },
            { "f", new IP(IF.IOP, (int)_c.SINE_FREQ, T.REAL, "The frequency in Hertz") },
            { "td", new IP(IF.IOP, (int)_c.SINE_DELAY, T.REAL, "The delay in seconds") },
            { "theta", new IP(IF.IOP, (int)_c.SINE_THETA, T.REAL, "The damping factor") }
        };

        /// <summary>
        /// Get the parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        /// <summary>
        /// Offset value
        /// </summary>
        public Parameter<double> VO { get; } = new Parameter<double>();

        /// <summary>
        /// Amplitude value
        /// </summary>
        public Parameter<double> VA { get; } = new Parameter<double>();

        /// <summary>
        /// Frequency
        /// </summary>
        public Parameter<double> Freq { get; } = new Parameter<double>();

        /// <summary>
        /// Delay
        /// </summary>
        public Parameter<double> Delay { get; } = new Parameter<double>();

        /// <summary>
        /// Damping factor
        /// </summary>
        public Parameter<double> Theta { get; } = new Parameter<double>();
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        public Sine() : base("Sine")
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="vo"></param>
        /// <param name="va"></param>
        /// <param name="freq"></param>
        /// <param name="td"></param>
        /// <param name="theta"></param>
        public Sine(double vo, double va, double freq, double td = 0.0, double theta = 0.0)
            : this()
        {
            VO.Par(vo);
            VA.Par(va);
            Freq.Par(freq);
            Delay.Par(td);
            Theta.Par(theta);
        }

        /// <summary>
        /// Set a parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.SINE_VO: VO.Par((double)value); break;
                case _c.SINE_VA: VA.Par((double)value); break;
                case _c.SINE_FREQ: Freq.Par((double)value); break;
                case _c.SINE_DELAY: Delay.Par((double)value); break;
                case _c.SINE_THETA: Theta.Par((double)value); break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Get a parameter
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.SINE_VO: return VO.Value;
                case _c.SINE_VA: return VA.Value;
                case _c.SINE_FREQ: return Freq.Value;
                case _c.SINE_DELAY: return Delay.Value;
                case _c.SINE_THETA: return Theta.Value;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Setup the waveform
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="ckt"></param>
        public override void Setup(Instance instance, Circuit ckt)
        {
            vo = VO;
            va = VA;
            freq = Freq;
            td = Delay;
            theta = Theta;
        }

        /// <summary>
        /// Calculate the sine at a specific time
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public override double At(double time)
        {
            time -= td;
            double result = 0.0;
            if (time <= 0.0)
                result = 0.0;
            else
                result = vo + va * Math.Sin(freq * time * (2.0 * Circuit.CONSTPI));
            return result;
        }

        /// <summary>
        /// Accept the current timepoint
        /// </summary>
        /// <param name="ckt"></param>
        public override void Accept(Circuit ckt)
        {
        }
    }
}
